home *** CD-ROM | disk | FTP | other *** search
- // This is part of the iostream library, providing input/output for C++.
- // Copyright (C) 1991 Per Bothner.
- //
- // This library is free software; you can redistribute it and/or
- // modify it under the terms of the GNU Library General Public
- // License as published by the Free Software Foundation; either
- // version 2 of the License, or (at your option) any later version.
- //
- // This library is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- // Library General Public License for more details.
- //
- // You should have received a copy of the GNU Library General Public
- // License along with this library; if not, write to the Free
- // Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- #include "ioprivat.h"
- #include <sys/file.h>
- #include <sys/stat.h>
- #include <errno.h>
-
- // An fstream can be in at most one of put mode, get mode, or putback mode.
- // Putback mode is a variant of get mode.
-
- // In a filebuf, there is only one current position, instead of two
- // separate get and put pointers. In get mode, the current posistion
- // is that of gptr(); in put mode that of pptr().
-
- // The position in the buffer that corresponds to the position
- // in external file system is file_ptr().
- // This is normally egptr(), except in putback mode, when it is _save_egptr.
- // If the field _fb._offset is >= 0, it gives the offset in
- // the file as a whole of the start of the buffer (base()).
-
- // PUT MODE:
- // If a filebuf is in put mode, pbase() is non-NULL and equal to base().
- // Also, epptr() == ebuf().
- // Also, eback() == gptr() && gptr() == egptr().
- // The un-flushed character are those between pbase() and pptr().
- // GET MODE:
- // If a filebuf is in get or putback mode, eback() != egptr().
- // In get mode, the unread characters are between gptr() and egptr().
- // The OS file position corresponds to that of egptr().
- // PUTBACK MODE:
- // Putback mode is used to remember "excess" characters that have
- // been sputbackc'd in a separate putback buffer.
- // In putback mode, the get buffer points to the special putback buffer.
- // The unread characters are the characters between gptr() and egptr()
- // in the putback buffer, as well as the area between save_gptr()
- // and save_egptr(), which point into the original reserve buffer.
- // (The pointers save_gptr() and save_egptr() are the values
- // of gptr() and egptr() at the time putback mode was entered.)
- // The OS position corresponds to that of save_egptr().
- //
- // LINE BUFFERED OUTPUT:
- // During line buffered output, pbase()==base() && epptr()==base().
- // However, ptr() may be anywhere between base() and ebuf().
- // This forces a call to filebuf::overflow(int C) on every put.
- // If there is more space in the buffer, and C is not a '\n',
- // then C is inserted, and pptr() incremented.
- //
- // UNBUFFERED STREAMS:
- // If a filebuf is unbuffered(), the _shortbuf[1] is used as the buffer.
-
- #ifdef atarist
- extern "C" { extern int __default_mode__; }
- #endif
-
- void filebuf::init()
- {
- _fb._fileno = -1;
- _fb._offset = 0;
- _fb._fake = 0;
- _flags |= _S_IS_FILEBUF;
- _fb._save_gptr = NULL;
-
- // Link in:
- _chain = __stream_list;
- __stream_list = this;
- _fb._fileno = -1;
- }
-
- filebuf::filebuf()
- {
- init();
- }
-
- filebuf::filebuf(int fd)
- {
- init();
- attach(fd);
- #ifdef atarist
- if((fd == 0) || (fd == 1) || (fd == 2))
- _flags &= (~_S_IS_BINARY);
- #endif
- }
-
- filebuf::filebuf(int fd, char* p, size_t len)
- {
- init();
- attach(fd);
- setbuf(p, len);
- #ifdef atarist
- if((fd == 0) || (fd == 1) || (fd == 2))
- _flags &= (~_S_IS_BINARY);
- #endif
- }
-
- filebuf::~filebuf()
- {
- if (!(_flags & _S_DELETE_DONT_CLOSE))
- close();
-
- // Unlink:
- streambuf **f;
- for (f = &__stream_list; *f != NULL; f = &(*f)->_chain) {
- if (*f = this) {
- *f = _chain;
- break;
- }
- }
- }
-
- filebuf* filebuf::open(const char *filename, int mode, int prot = 0664)
- {
- if (is_open())
- return NULL;
- int posix_mode;
- int read_write;
- if ((mode & (ios::in|ios::out)) == (ios::in|ios::out))
- posix_mode = O_RDWR, read_write = _S_CAN_READ+_S_CAN_WRITE;
- else if (mode & (ios::out|ios::app))
- posix_mode = O_WRONLY, read_write = _S_CAN_WRITE;
- else if (mode & (int)ios::in)
- posix_mode = O_RDONLY, read_write = _S_CAN_READ;
- else
- posix_mode = 0, read_write = 0;
- if ((mode & (int)ios::trunc) || mode == (int)ios::out)
- posix_mode |= O_TRUNC;
- if (mode & ios::app)
- posix_mode |= O_APPEND;
- if (!(mode & (int)ios::nocreate) && mode != ios::in)
- posix_mode |= O_CREAT;
- if (mode & (int)ios::noreplace)
- posix_mode |= O_EXCL;
- int fd = ::open(filename, posix_mode, prot);
- if (fd < 0)
- return NULL;
- _fb._fileno = fd;
- _fb._offset = 0;
- _fb._fake = 0;
- _flags |= read_write;
- #ifdef atarist
- if(mode & 128)
- _flags |= _S_IS_BINARY;
- else if(mode & 256)
- _flags &= (~_S_IS_BINARY);
- #endif
- if (mode & ios::ate) {
- if (seekoff(0, ios::end) == EOF)
- return NULL;
- }
- return this;
- }
-
- filebuf* filebuf::open(const char *filename, const char *mode)
- {
- if (is_open())
- return NULL;
- int oflags = 0, omode;
- int read_write;
- int oprot = 0666;
- #ifdef atarist
- int text_mode = 0;
- #endif
- switch (*mode++) {
- case 'r':
- omode = O_RDONLY;
- read_write = _S_CAN_READ;
- break;
- case 'w':
- omode = O_WRONLY;
- oflags = O_CREAT|O_TRUNC;
- read_write = _S_CAN_WRITE;
- break;
- case 'a':
- omode = O_WRONLY;
- oflags = O_CREAT|O_APPEND;
- read_write = _S_CAN_WRITE;
- break;
- default:
- errno = EINVAL;
- return NULL;
- }
- #ifndef atarist
- if (mode[0] == '+' || (mode[0] == 'b' && mode[1] == '+')) {
- omode = O_RDWR;
- read_write = _S_CAN_READ+_S_CAN_WRITE;
- }
- #else
- while(*mode)
- {
- switch(*mode++)
- {
- case '+':
- omode = O_RDWR;
- read_write = _S_CAN_READ+_S_CAN_WRITE;
- break;
- case 'b':
- text_mode = 2;
- break;
- case 't':
- text_mode = 1;
- break;
- default:
- errno = EINVAL;
- return NULL;
- }
- }
- #endif
-
- int fdesc = ::open(filename, omode|oflags, oprot);
- if (fdesc < 0)
- return NULL;
- _fb._fileno = fdesc;
- _fb._offset = 0;
- _fb._fake = 0;
- _flags |= read_write;
- #ifdef atarist
- if(text_mode == 2)
- _flags |= _S_IS_BINARY;
- else if(text_mode == 1)
- _flags &= (~_S_IS_BINARY);
- #endif
- return this;
- }
-
- filebuf* filebuf::attach(int fd)
- {
- if (is_open())
- return NULL;
- _fb._fileno = fd;
- _flags |= (_S_CAN_READ+_S_CAN_WRITE+_S_DELETE_DONT_CLOSE);
-
- return this;
- }
-
- int filebuf::overflow(int c)
- {
- if (pptr() == pbase() && c == EOF)
- return 0;
- if ((_flags & _S_CAN_WRITE) == 0) // SET ERROR
- return EOF;
- if (is_reading()) {
- if (pptr() != gptr() && pptr() > pbase())
- if (do_flush())
- return EOF;
- setp(gptr(), ebuf());
- setg(egptr(), egptr(), egptr());
- }
- if (allocate() > 0) {
- if (_flags & _S_LINE_BUF) setp(base(), base());
- else setp(base(), ebuf());
- setg(pbase(), pbase(), pbase());
- }
- int flush_only;
- if (c == EOF) flush_only = 1, c = 0;
- else flush_only = 0;
- if (epptr() == pbase()) { // Line buffering
- if (pptr() < ebuf() && !flush_only) {
- *_pptr++ = c;
- if (c != '\n')
- return (unsigned char)c;
- else
- flush_only = 1;
- }
- }
- size_t to_do = out_waiting();
- if (to_do > 0) {
- char *ptr = pbase();
- for (;;) {
- long count = ::write(fd(), ptr, to_do);
- if (count == EOF)
- return EOF;
- _fb._offset += count;
- to_do -= count;
- if (to_do == 0)
- break;
- ptr += count;
- }
- if (_flags & _S_LINE_BUF) setp(pbase(), pbase());
- else setp(pbase(), epptr());
- setg(egptr(), egptr(), egptr());
- }
- if (flush_only)
- return c;
- else
- return sputc(c);
- }
-
- int filebuf::underflow()
- {
- #if 0
- /* SysV does not make this test; take it out for compatibility */
- if (fp->_flags & __SEOF)
- return (EOF);
- #endif
-
- if ((_flags & _S_CAN_READ) == 0) // SET ERROR
- return EOF;
- retry:
- if (gptr() < egptr())
- return *(unsigned char*)gptr();
- if (_fb._save_gptr) { // Free old putback buffer.
- if (eback() != _fb._shortbuf)
- free(eback());
- _fb._save_gptr = NULL;
- setg(base(), _fb._save_gptr, _fb._save_egptr); // Restore get area.
- goto retry;
- }
-
- allocate();
- #if 0
- /* if not already reading, have to be reading and writing */
- if ((fp->_flags & __SRD) == 0) {
- if ((fp->_flags & __SRW) == 0)
- return (EOF);
- /* switch to reading */
- if (fp->_flags & __SWR) {
- if (fflush(fp))
- return (EOF);
- fp->_flags &= ~__SWR;
- fp->_w = 0;
- fp->_lbfsize = 0;
- }
- fp->_flags |= __SRD;
- } else {
- // We were reading. If there is an ungetc buffer,
- // we must have been reading from that. Drop it,
- // restoring the previous buffer (if any). If there
- // is anything in that buffer, return.
- if (HASUB(fp)) {
- FREEUB(fp);
- if ((fp->_r = fp->_ur) != 0) {
- fp->_p = fp->_up;
- return (0);
- }
- }
- }
- #endif
- if ((_flags & _S_LINE_BUF) || unbuffered()) {
- // Flush all line buffered files before reading.
- for (streambuf* fp = __stream_list; fp != NULL; fp = fp->_chain)
- if (fp->linebuffered())
- fp->sync();
- }
- if (pptr() > pbase())
- if (do_flush()) return EOF;
- int count = sys_read(base(), ebuf() - base());
- if (count <= 0) {
- if (count == 0)
- _flags |= _S_EOF_SEEN;
- else
- _flags |= _S_ERR_SEEN, count = 0;
- return EOF;
- }
- setg(base(), base(), base() + count);
- return *(unsigned char*)gptr();
- }
-
- int filebuf::pbackfail(int c)
- {
- if (is_writing()) {
- overflow(EOF);
- // FIXME: check for writing!
- }
- if (_fb._save_gptr == NULL) { // No putback buffer.
- _fb._save_gptr = gptr(); _fb._save_egptr = egptr();
- setg(_fb._shortbuf, _fb._shortbuf+1, _fb._shortbuf+1);
- }
- else { // Increase size of existing putback buffer.
- size_t new_size;
- size_t old_size = egptr() - eback();
- new_size = eback() == _fb._shortbuf ? 128 : 2 * old_size;
- char* new_buf = new char[new_size];;
- memcpy(new_buf+(new_size-old_size), eback(), old_size);
- if (eback() != _fb._shortbuf)
- delete eback();
- setg(new_buf, new_buf+(new_size-old_size), new_buf+new_size);
- }
- gbump(-1);
- *gptr() = c;
- return (unsigned char)c;
- }
-
- int filebuf::do_flush()
- {
- if (egptr() != pbase()) {
- long new_pos = sys_seek(pbase()-egptr(), ios::cur);
- if (new_pos == -1)
- return EOF;
- }
- long to_do = pptr()-pbase();
- char* ptr = pbase();
- while (to_do > 0) {
- long count = sys_write(ptr, to_do);
- if (count == EOF)
- return EOF;
- if (_fb._offset >= 0)
- _fb._offset += count;
- to_do -= count;
- ptr += count;
- }
- setg(base(), pptr(), pptr());
- setp(NULL, NULL); // ????
- return 0;
- }
-
- int filebuf::sync()
- {
- // char* ptr = cur_ptr();
- if (pptr() > pbase())
- do_flush();
- if (gptr() != egptr()) {
- if (sys_seek(gptr() - egptr(), ios::cur) == EOF)
- return EOF;
- }
- // FIXME: Handle putback mode!
- // setg(base(), ptr, ptr);
- return 0;
- }
-
- streamoff filebuf::seekoff(streamoff offset, seek_dir dir, int mode)
- {
- #ifdef atarist
- long result, new_offset, delta;
- #else
- fpos_t result, new_offset, delta;
- #endif
- int count;
- if (unbuffered())
- goto dumb;
- // FIXME: WHat if buffer already allocated?
- switch (dir) {
- case ios::cur:
- if (_fb._offset < 0) {
- _fb._offset = sys_seek(0, ios::cur);
- if (_fb._offset < 0)
- return EOF;
- }
- offset += _fb._offset + (file_ptr()-base());
- if (_fb._save_gptr)
- offset -= egptr() - gptr(); // Subtract putback size.
- dir = ios::beg;
- break;
- case ios::beg:
- break;
- case ios::end:
- struct stat st;
- if (sys_stat(&st) == 0 && (st.st_mode & S_IFMT) == S_IFREG) {
- offset += st.st_size;
- dir = ios::beg;
- }
- else
- goto dumb;
- }
- // At this point, dir==ios::beg.
- // FIXME: Handle case of there being a putback buffer!
- // If destination is within current buffer, optimize:
- if (_fb._offset >= 0) {
- long rel_offset = (long)(offset - _fb._offset); // Offset relative to base().
- if (rel_offset >= 0
- // should be: <= max(egptr(),pptr()-base() ???
- && rel_offset <= (long)(egptr()-base())) {
- setg(base(), base() + rel_offset, egptr());
- return offset;
- }
- }
- if (pptr() > pbase()) {
- do_flush();
- }
- // Try to seek to a block boundary, to improve kernal page management.
- new_offset = offset & ~(ebuf() - base() - 1);
- delta = offset - new_offset;
- if (delta > ebuf() - base()) {
- new_offset = offset;
- delta = 0;
- }
- result = sys_seek(new_offset, ios::beg);
- if (result < 0)
- return EOF;
- _fb._offset = result;
- count = sys_read(base(), ebuf()-base());
- if (count < 0)
- return EOF;
- setg(base(), base(), base()+count);
- _flags &= ~ _S_EOF_SEEN;
- return result;
- dumb:
- // Maybe just do sync?
- if (pptr() > pbase()) {
- do_flush();
- }
- if (_fb._save_gptr != NULL) { // Get rid of putback buffer.
- if (eback() != _fb._shortbuf)
- free(eback());
- _fb._save_gptr = NULL;
- }
- result = sys_seek(offset, dir);
- if (result != EOF) {
- _flags &= ~_S_EOF_SEEN;
- }
- char* start = unbuffered() ? _fb._shortbuf : base();
- setg(start, start, start);
- setp(start, start);
- return result;
- }
-
- filebuf* filebuf::close()
- {
- if (!is_open())
- return NULL;
-
- // Flush.
- overflow(EOF);
-
- /* Free the buffer's storage. */
- // Or should that be done only on destruction???
- if (_base != NULL && !(_flags & _S_USER_BUF)) {
- free(_base);
- _base = NULL;
- }
-
- int status = sys_close();
-
- _flags = 0;
- _fb._fileno = EOF;
-
- return status < 0 ? NULL : this;
- }
-
- #ifndef atarist
- int filebuf::sys_read(char* buf, size_t size)
- {
- return ::read(_fb._fileno, buf, size);
- }
- fpos_t filebuf::sys_seek(fpos_t offset, seek_dir dir)
- {
- return ::lseek(fd(), offset, (int)dir);
- }
-
- #else
- long filebuf::sys_read(char* buf, size_t size)
- {
- return ::_read(_fb._fileno, buf, size);
- }
-
- long filebuf::sys_seek(long offset, seek_dir dir)
- {
- return ::lseek(fd(), offset, (int)dir);
- }
- #endif
-
-
- long filebuf::sys_write(const void *buf, long n)
- {
- #ifndef atarist
- return ::write(fd(), buf, n);
- #else
- return ::_write(fd(), buf, n);
- #endif
- }
-
- int filebuf::sys_stat(void* st)
- {
- return ::_fstat(fd(), (struct stat*)st);
- }
-
- int filebuf::sys_close()
- {
- return ::close(fd());
- }
-